package com.ms.service.module.baccarat.db;

import java.math.BigDecimal;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.bank.BankMoneyChangeWay;
import com.ms.game.bank.db.BankDAO;
import com.ms.game.bank.db.BankLogModel;
import com.ms.game.common.GameType;
import com.ms.game.common.container.BasicSettingUtil;
import com.ms.player.Player;
import com.ms.player.PlayerUserType;
import com.ms.service.module.analysisgroup.AnalysisGroupProcessor;
import com.ms.service.module.baccarat.BacrBetPlayerInfo;
import com.ms.service.module.baccarat.BacrGameCore;
import com.ms.service.module.baccarat.BacrProcessor;
import com.ms.service.module.datacenterlite.CentralAsyncCommand;
import com.ms.service.module.zone.module.BasicZoneModule;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.datamanager.BaseDataInfo;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoin;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeWay;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;
import com.xpec.c4.service.module.datacenter.player.UpdatePlayerMoneyAsync;

/**
 * 百家樂專用處理玩家得分，送到身上或保險箱
 * 
 * @author TW15010
 *
 */
public class BacrSendScoreToPlayerCentralAsync extends CentralAsyncCommand {
	private static final long serialVersionUID = -1615667624935221982L;

	/**
	 * 百家樂相關得分追蹤用關鍵字
	 */
	public static final String LOG_TRACE_HEADER = "BacrMoneyLogTrace ";

	/** log4j object */
	private static final Log log = LogFactory.getLog(BacrSendScoreToPlayerCentralAsync.class);

	// ====================================================================
	// 外部輸入參數

	/**
	 * 牌桌 id
	 */
	private String tableId;

	/**
	 * 發出要求的 module id
	 */
	private String srcModuleId;

	/**
	 * 百家樂玩家資訊
	 */
	private BacrBetPlayerInfo playerInfo;

	/**
	 * 使用幣別
	 */
	private CoinType coinType;

	/**
	 * 指定幣別目前在身上的金額
	 */
	private BigDecimal currentValue;

	/**
	 * 百家樂得分
	 */
	private BigDecimal score;

	/**
	 * 活動連勝得分
	 */
	private BigDecimal comboScore;

	// ====================================================================
	// 執行後產生資料

	/**
	 * 是否要處理得分
	 */
	private boolean haveScore;

	/**
	 * 得分是否要進保險箱
	 */
	private boolean haveScoreToBank;

	/**
	 * 得分是否要進玩家身上
	 */
	private boolean haveScoreToPlayer;

	/**
	 * 是否要處理活動連勝得分
	 */
	private boolean haveComboScore;

	/**
	 * 是否有要對保險鄉進行操作
	 */
	private boolean haveBankProc;

	/**
	 * 處理玩家得分進口袋要用的資料
	 */
	private BaseDataInfo baseDataInfo;

	/**
	 * 保險箱處理分數前的值
	 */
	private BigDecimal bankBeforeScore;

	/**
	 * 保險箱處理分數後的值
	 */
	private BigDecimal bankAfterScore;

	/**
	 * 保險箱處理活動連勝分數前的值
	 */
	private BigDecimal bankBeforeComboScore;

	/**
	 * 保險箱處理活動連勝分數後的值
	 */
	private BigDecimal bankAfterComboScore;

	/**
	 * 保險箱最後更新完的值
	 */
	private BigDecimal bankFinalValue;

	/**
	 * 更新保險箱的結果<br>
	 * true 成功<br>
	 * false 失敗
	 */
	private boolean bankProcResult;

	/**
	 * 設定初始參數
	 * 
	 * @param tableId
	 *            牌桌 id
	 * @param srcModuleId
	 *            發出要求的 module id
	 * @param betPlayerInfo
	 *            百家樂押注玩家基本資訊
	 * @param coinType
	 *            使用幣別
	 * @param currentValue
	 *            指定幣別目前在身上的金額
	 * @param score
	 *            百家樂得分, 大於 0 就會處理
	 * @param scoreToBank
	 *            得分是否送到保險箱
	 * @param activityComboScore
	 *            活動連勝得分, 大於 0 就會處理
	 * @return
	 */
	public boolean setParameters(String tableId, String srcModuleId, BacrBetPlayerInfo betPlayerInfo,
			CoinType coinType, BigDecimal currentValue, int score, boolean scoreToBank, int activityComboScore) {
		this.tableId = tableId;
		this.srcModuleId = srcModuleId;

		// 沒有玩家基本資訊
		if (betPlayerInfo == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + ": betPlayerInfo=null");
			}
			return false;
		}
		this.playerInfo = betPlayerInfo;

		// 沒有指定幣別
		if (coinType == null) {
			if (log.isErrorEnabled()) {
				log.error(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + ": p:" + betPlayerInfo.getPlayerId()
						+ " coinType=null");
			}
			return false;
		}

		// 錯誤的幣別
		switch (coinType) {
		case GOLD:
		case TRIAL:
			this.coinType = coinType;
			break;
		default:
			if (log.isErrorEnabled()) {
				log.error(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + ": p:" + betPlayerInfo.getPlayerId()
						+ " invalid coin type " + coinType);
			}
			return false;
		}

		this.currentValue = currentValue;
		this.score = new BigDecimal(score);
		this.comboScore = new BigDecimal(activityComboScore);

		haveScore = (score > 0);
		haveScoreToBank = (haveScore && scoreToBank);
		haveScoreToPlayer = (haveScore && !scoreToBank);
		haveComboScore = (activityComboScore > 0);
		haveBankProc = (haveScoreToBank || haveComboScore);

		// 至少要有百家樂本身的分數
		if (!haveScore) {
			if (log.isErrorEnabled()) {
				log.error(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + ": p:" + betPlayerInfo.getPlayerId()
						+ " score=0");
			}
			return false;
		}

		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
			StringBuilder sb = new StringBuilder();
			sb.append(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + ": p:" + betPlayerInfo.getPlayerId()
					+ " ");
			sb.append("score=" + score + " ");
			sb.append("toBank=" + scoreToBank + " ");

			if (haveComboScore) {
				sb.append("comboScore=" + comboScore);
			}

			log.info(sb.toString());
		}

		return true;
	}

	@Override
	public void asyncExecute() throws Exception {
		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
			log.info(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + " p:" + playerInfo.getPlayerId()
					+ " begin");
		}

		if (haveBankProc) {
			// 計算要進保險箱的總金額
			BigDecimal totalToBank = BigDecimal.ZERO;

			BankLogModel scoreToBankLog = null;
			BankLogModel comboScoreToBankLog = null;

			// 有分數要進保險箱
			if (haveScoreToBank) {
				totalToBank = Helper.add(totalToBank, score);
			}

			// 有活動連勝得分
			if (haveComboScore) {
				totalToBank = Helper.add(totalToBank, comboScore);
			}

			// 更新到保險箱
			BigDecimal updateFailValue = new BigDecimal(-1);
			BigDecimal updatedBankValue = BankDAO.UpdateBankMoney(playerInfo.getPlayerId(), totalToBank);
			bankFinalValue = updatedBankValue;

			// 更新失敗
			if (Helper.isEqual(updatedBankValue, updateFailValue)) {
				bankProcResult = false;

				if (log.isErrorEnabled()) {
					log.error("update player " + playerInfo.getPlayerId() + " score to bank fail.");
				}
			} else { // 更新成功
				bankProcResult = true;

				if (haveComboScore) {
					// 計算變更前後金額
					bankAfterComboScore = updatedBankValue;

					updatedBankValue = Helper.sub(updatedBankValue, comboScore);
					bankBeforeComboScore = updatedBankValue;

					// 產生 log
					comboScoreToBankLog = new BankLogModel();
					comboScoreToBankLog.setOrderId(playerInfo.getOrderId().toString());
					comboScoreToBankLog.setAccountId(playerInfo.getAccountId().toString());
					comboScoreToBankLog.setPlayerId(playerInfo.getPlayerId().toString());
					comboScoreToBankLog.setPlayerName(playerInfo.getPlayerName());
					comboScoreToBankLog.setChangeWay(BankMoneyChangeWay.ADD_BACARRAT_ACTIVITY_COMBO_SCORE.getId());
					comboScoreToBankLog.setChangeWayDesc(BankMoneyChangeWay.ADD_BACARRAT_ACTIVITY_COMBO_SCORE
							.getDesc());
					comboScoreToBankLog.setBankGoldChangeValue(comboScore);
					comboScoreToBankLog.setBankGoldBeforeChange(bankBeforeComboScore);
					comboScoreToBankLog.setBankGoldAfterChange(bankAfterComboScore);
				}

				if (haveScoreToBank) {
					// 計算變更前後金額
					bankAfterScore = updatedBankValue;

					updatedBankValue = Helper.sub(updatedBankValue, score);
					bankBeforeScore = updatedBankValue;

					// 產生 log
					scoreToBankLog = new BankLogModel();
					scoreToBankLog.setOrderId(playerInfo.getOrderId().toString());
					scoreToBankLog.setAccountId(playerInfo.getAccountId().toString());
					scoreToBankLog.setPlayerId(playerInfo.getPlayerId().toString());
					scoreToBankLog.setPlayerName(playerInfo.getPlayerName());
					scoreToBankLog.setChangeWay(BankMoneyChangeWay.ADD_BACARRAT_SCORE.getId());
					scoreToBankLog.setChangeWayDesc(BankMoneyChangeWay.ADD_BACARRAT_SCORE.getDesc());
					scoreToBankLog.setBankGoldChangeValue(score);
					scoreToBankLog.setBankGoldBeforeChange(bankBeforeScore);
					scoreToBankLog.setBankGoldAfterChange(bankAfterScore);
					//送到任務檢查得分
					sendMissionScoreCheck(score);
				}

				// 照順序寫入 log
				if (haveScoreToBank) {
					getCurrentModuleServer().saveDbLog(scoreToBankLog);
				}

				if (haveComboScore) {
					getCurrentModuleServer().saveDbLog(comboScoreToBankLog);
				}

				if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
					log.info(getBankUpdateResultString());
				}
				
				// 進保險箱的錢也提供給統計
				if(Helper.isGreater(totalToBank, BigDecimal.ZERO)){
					sendBackScoreToAnalysis(totalToBank);
				}
				
			} // if (update money to bank success)
		}

		// 有分數要到玩家身上
		if (haveScoreToPlayer) {
			ChangeCoinLister coinLister = new ChangeCoinLister();
			coinLister.addCoin(coinType, score);
			ChangeCoin coinInfo = coinLister.getCoinMap().get(coinType);
			coinInfo.setCurrentValue(currentValue);

			MoneyChangeParam param = new MoneyChangeParam();
			param.setJpScore(BigDecimal.ZERO);
			param.setOrderId(playerInfo.getOrderId());
			param.setChangeWay(MoneyChangeWay.ADD_SCORE);
			param.setChangeCoinLister(coinLister);
			param.setGameId(GameType.BACR.name());
			param.setGameName(BasicSettingUtil.getGameTypeNameByStringTable(GameType.BACR.name()));
			param.setGameHallId(tableId);
			param.setGameHallName("");
			param.setAccountId(playerInfo.getAccountId());
			param.setPlayerId(playerInfo.getPlayerId());
			param.setPlayerName(playerInfo.getPlayerName());
			param.setUserType(playerInfo.getUserType());
			param.setSrcModuleId(srcModuleId);
			param.setOfflineSpin(false);
			param.setChangeTime(TimeSys.currentTimeMills());


			baseDataInfo = new BaseDataInfo();
			baseDataInfo.setData(param);

			UpdatePlayerMoneyAsync task = new UpdatePlayerMoneyAsync(null, getCurrentModuleServer());
			task.setDataInfo(baseDataInfo);

			task.asyncExecute();
			task.execute();

			baseDataInfo.setResultCode(task.getResultCode());

			if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
				log.info(LOG_TRACE_HEADER + param.getResultString());
			}
		}

		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
			log.info(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + " p:" + playerInfo.getPlayerId()
					+ " finish");
		}
	}

	/**
	 * @param userData
	 *            custom params<br>
	 *            [0] BacrProcessor proc<br>
	 *            [1] BacrGameCore core<br>
	 *            [2] GUID playerId
	 */
	@Override
	public void callBackExecute(Object... userData) throws Exception {
		BacrProcessor proc = (BacrProcessor) userData[0];
		BacrGameCore core = (BacrGameCore) userData[1];
		GUID playerId = (GUID) userData[2];

		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
			log.info(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + " p:" + playerInfo.getPlayerId()
					+ " begin");
		}

		BasicServiceModule module = getCurrentModuleServer();
		if (!(module instanceof BasicZoneModule)) {
			if (log.isErrorEnabled()) {
				log.error(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + " p:" + playerInfo.getPlayerId()
						+ " current module " + module.getModuleName() + " is not a zone.");
			}
			return;
		}

		BasicZoneModule zoneModule = (BasicZoneModule) module;

		// 錢更新到玩家身上後的後續處理
		if (haveScoreToPlayer) {
			zoneModule.getPlayerDataAgent().updatePlayerMoneyToDcPreProcCallback(baseDataInfo);
		}

		// 錢更新到保險箱的後續處理
		// 如果線上有角色資料
		if (haveBankProc) {
			if (bankProcResult) {
				Player onlinePlayerData = zoneModule.getZone().getPlayer(playerInfo.getPlayerId());
				if (onlinePlayerData != null) {
					// 更新回 zone 上的 player
					onlinePlayerData.setBankGoldCoin(bankFinalValue);
				}

				// 開啟 client 一定要重新要保險箱金額的旗標
				GUID session = PlayerCommonInfoManager.getInstance().getSessionByPlayerGuid(playerId);
				if (session != null) {
					MessageAction.sendMessage(session, MessageTypes.S_ZONE_MODIFY_BANK_INFO_FLAG, "");
				}
			}
		}

		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(playerInfo.getUserType())) {
			if (haveScoreToPlayer) {
				MoneyChangeParam param = (MoneyChangeParam) baseDataInfo.getData();
				log.info(LOG_TRACE_HEADER + param.getResultString());
			}

			if (haveBankProc) {
				log.info(getBankUpdateResultString());
			}

			log.info(LOG_TRACE_HEADER + GameUtility.getCurMethodName() + " p:" + playerInfo.getPlayerId()
					+ " finish");
		}

		// call 回 processor
		proc.onSendScoreToPlayerCallback(core, playerId);
	}

	/**
	 * 取得更新錢到保險箱的結果字串
	 * 
	 * @return
	 */
	private String getBankUpdateResultString() {
		StringBuilder sb = new StringBuilder();
		sb.append(LOG_TRACE_HEADER + "update player " + playerInfo.getPlayerId() + " score to bank success="
				+ bankProcResult + " ");

		// 成功
		if (bankProcResult) {
			if (haveScoreToBank) {
				sb.append("score " + bankBeforeScore + "+" + score + "=" + bankAfterScore + " ");
			}

			if (haveComboScore) {
				sb.append("comboScore " + bankBeforeComboScore + "+" + comboScore + "=" + bankAfterComboScore
						+ " ");
			}
		} else { // 失敗
			if (haveScoreToBank) {
				sb.append("score " + score + " ");
			}

			if (haveComboScore) {
				sb.append("comboScore " + comboScore + " ");
			}
		}

		return sb.toString();
	}
	
	/**
	 * 送到任務檢查得分
	 */
	private void sendMissionScoreCheck(BigDecimal score){
	}
	
	/**
	 * 送保險箱的分數給統計
	 */
	private void sendBackScoreToAnalysis(BigDecimal bankScore){
		if(playerInfo == null || playerInfo.getUserType() != PlayerUserType.PLAYER){
			return;
		}
		
		try {
			GUID orderID = playerInfo.getOrderId();
			BacrPlayerLogModel playerLogModel = playerInfo.getLogModel();
			String hallType = (playerLogModel == null) ? "NULL" : playerLogModel.getTableId();
			
			AnalysisGroupProcessor.sendGameRevenueAndExpenditureToAnalysisGroup
			(
					orderID,
					GameType.BACR.name(),
					hallType,
					false, //對遊戲而言算是支出
					coinType.equals(CoinType.GOLD), //目前只有判斷gold是正式幣
					false,
					bankScore,
					false
			);
			
		} catch (Exception e) {
			if(log.isErrorEnabled()){
				log.error("sendBackScoreToAnalysis fail", e);
			}
		}
	}
}